import DbExecutor from "../DbExecutor";
import Field from '../../utils/Field';
import Session from "../Session";
import Entity from "../Entity";
import IExecutor from '../IExecutor'
import DataType from "../../utils/DataType";
import RowDataPacket from './RowDataPacket'
import * as _ from 'lodash'
export default class MySQLExecutor extends DbExecutor implements IExecutor {

    constructor(session: Session) {
        super(session);
    }

    executeQuerySQL(entity: Entity, fields: Field[], table: string, id: any, cb?: Function) {

        let record = fields.find(function (item, index) {
            return item.isKey
        })

        if (!!!record) {
            return Promise.reject("NO PRIMARY KEY")
        }

        let sql = 'SELECT * FROM ' + table + ' WHERE ' + record.field + '=' + id

        let self = this
        self.session.queryOne(sql).then(function (resp) {
            let prop = entity['__dirty__'];
            fields.forEach(el => {
                prop['__' + el.propertyKey] = entity[el.propertyKey] = resp[el.field]
            });
            cb && cb(null, entity);
        }).catch(function (err) {
            console.log(err)
            cb && cb(err)
        })
    }


    executeUpdateDDL(table: string, fields: Field[]) {
        // show columns from user
        let self = this;
        let sql = 'show columns from ' + table;
        this.session.query(sql).then(function (columns) {
            self._updateTable(table, columns, fields)
        }).catch(function (err) {
            if (err && err.code == 'ER_NO_SUCH_TABLE') {
                //新建模型
                self._createTable(table, fields);
            }
        })
    }

    private _eqFieldAndColumn(field: Field, column: RowDataPacket) {
        let ret = true

        let _t = this._toMySQLType(field.dataType, field.length)

        if (_t != column.Type) {
            ret = false
        } else if ((field.isKey && column.Key != 'PRI')
            || (!field.isKey && column.Key == 'PRI')) {
            ret = false
        }

        return ret
    }

    private _updateTable(table: string, columns: RowDataPacket[], fields: Field[]) {
        //比对 columns与fields，找出两者的差集
        let self = this;
        let newList: Field[] = []
        let modifyList: Field[] = []

        let tmpColumns = _.cloneDeep(columns);
        fields.forEach(function (item, index) {
            var x = -1;
            let column = tmpColumns.find(function (col, index) {
                x = index;
                return item.field == col.Field
            })
            //找到了
            if (!!column) {
                //判断是否需要修改
                if (!self._eqFieldAndColumn(item, column)) {
                    modifyList.push(item)
                }
                //把item删除
                tmpColumns.splice(x, 1);
            }
            else { //未找到，说明item是新增的
                newList.push(item);
            }
        })
        let delList: RowDataPacket[] = tmpColumns;
        //先修改、删除、最后增加
        let modifySQL = [];
        modifyList.forEach(function (item) {
            //ALTER TABLE employee MODIFY job VARCHAR(20);
            modifySQL.push('ALTER TABLE ' + table + ' MODIFY ' + item.field + self._optAttrField(item))
            if (item.isKey) {
                modifySQL.push('ALTER TABLE ' + table + ' DROP PRIMARY KEY ,ADD PRIMARY KEY ( ' + item.field + ' )')
            }
        })

        delList.forEach(function (item) {
            //ALTER TABLE employee DROP address;
            modifySQL.push('ALTER TABLE ' + table + ' DROP ' + item.Field)
        })

        newList.forEach(function (item) {
            //ALTER TABLE employee DROP address;
            modifySQL.push('ALTER TABLE ' + table + ' ADD ' + item.field + self._optAttrField(item))
            if (item.isKey) {
                modifySQL.push('ALTER TABLE ' + table + ' DROP PRIMARY KEY ,ADD PRIMARY KEY ( ' + item.field + ' )')
            }
        })
        if (modifySQL.length > 0) {
            let msql = modifySQL.join(';')
            console.log(msql)
            this.session.query(msql + ';').catch(function (err) {
                console.log(err)
            })
        }

    }

    private _createTable(table: string, fields: Field[]) {
        //create table employee(id int,name varchar(20),sex char(2),birthday date,job varchar(50),salary double(7,2));
        let self = this;
        let prefix = []
        prefix.push('create')
        prefix.push('table')
        prefix.push(table + '(')
        let f = []
        let key = [];
        fields.forEach(function (item, index) {
            let it = item.field + ' '
            if (item.isKey) {
                key.push(item.field)
            }
            it += self._optAttrField(item)
            f.push(it)
        })
        //增加主键
        let _key = key.join(',')

        f.push('PRIMARY KEY (' + _key + ')')

        prefix.push(f.join(','))
        prefix.push(') ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0')
        let _sql = prefix.join(' ')
        console.log(_sql)
        this.session.query(_sql).catch(function (e) { console.log(e) })
    }

    private _toMySQLType(type: string, len: number) {
        let it = ''
        switch (type) {
            case DataType.String:
                it += 'varchar(' + len + ')'
                break;
            case DataType.Integer:
                it += 'int(' + len + ')'
                break;
            case DataType.Boolean:
                it += 'tinyint(1)'
                break;
            case DataType.BigInt:
                it += 'bigint(' + len + ')'
                break;
            case DataType.DateTime:
                it += 'datetime'
                break;
            case DataType.Long:
                it += 'bigint(' + len + ')'
                break;
            default: break;
        }

        return it
    }

    private _optAttrField(item: Field) {
        let it = ' ';
        if (item.describe) {
            it += item.describe
        } else {
            it += this._toMySQLType(item.dataType, item.length);
            if (item.isKey) {
                it += ' NOT NULL'
                if (item.dataType == DataType.Long
                    || item.dataType == DataType.BigInt
                    || item.dataType == DataType.Integer) {
                    it += ' AUTO_INCREMENT'
                }
            }
        }
        return it + ' '
    }

    executeSaveSQL(entity: Entity, fields: Field[], record: Field, table: string, cb?: Function) {
        let q = "";
        let prop = entity['__dirty__'];
        let r = prop['__' + record.field]; // 判断是否存在ID
        if (r) {
            // 使用更新
            let keys = [];
            let params = {};
            keys.push('update');
            keys.push(table);
            keys.push('set');

            let _fields = [];
            fields.forEach(el => {
                //不相同，则设置需要变更
                if (prop['__' + el.propertyKey] != entity[el.propertyKey]) {
                    _fields.push(el.field + '=#' + el.propertyKey + '#,');
                    params[el.propertyKey] = entity[el.propertyKey];
                }
            });
            if (_fields.length > 0) {
                let __field = _fields.join('');
                __field = __field.substr(0, __field.length - 1);
                keys.push(__field);
                keys.push('where')
                keys.push(record.field + '=' + r)
                q = keys.join(' ');
                let rx = this.session.update(q, params);
                rx.then(function (resp) {
                    // cb && cb();
                    //更新完成，把旧数据赋值新值
                    fields.forEach(el => {
                        //不相同，则发生表更
                        if (prop['__' + el.propertyKey] != entity[el.propertyKey]) {
                            prop['__' + el.propertyKey] = entity[el.propertyKey];
                        }
                    });
                    cb && cb(null, entity);
                }).catch(function (e) {
                    console.log(e)
                    cb && cb(e)
                })
            }

        } else {
            let keys = [];
            keys.push('insert into');
            keys.push(table + '(');
            let _fields = [];
            let params = {};
            let _params = [];
            fields.forEach(el => {
                if (!el.isKey) {
                    _fields.push(el.field + ',');
                    params[el.propertyKey] = entity[el.propertyKey];
                    _params.push('#' + el.propertyKey + '#,')
                }
            });
            if (_fields.length > 0) {
                let __field = _fields.join('');
                __field = __field.substr(0, __field.length - 1);
                keys.push(__field);
                keys.push(')');
                keys.push('values(')
                let __params = _params.join('');
                __params = __params.substr(0, __params.length - 1);
                keys.push(__params)
                keys.push(')')
                q = keys.join(' ');
                let rx = this.session.create(q, params);
                rx.then(function (resp) {
                    fields.forEach(el => {
                        if (el.field == record.field) {
                            entity[el.propertyKey] = prop['__' + record.field] = resp.insertId
                        } else {
                            prop['__' + el.propertyKey] = entity[el.propertyKey];
                        }
                    });
                    cb && cb(null, entity);
                }).catch(function (e) {
                    console.log(e)
                    cb && cb(e)
                })

            }
        }
    }

    executeDeleteSQL(entity: Entity, record: Field, table: string, cb?: Function) {
        let prop = entity['__dirty__'];
        let xid = prop['__' + record.field]; // 判断是否存在ID
        if (xid) {
            let keys = []
            let params = {}
            keys.push('delete from')
            keys.push(table)
            keys.push('where')
            keys.push(record.field)
            keys.push('=')
            keys.push(xid)
            let rx = this.session.delete(keys.join(' '), params);
            rx.then(function (resp) {
                prop['__' + record.field] = null
                cb && cb()
            }).catch(function (e) {
                console.log(e)
                cb && cb(e)
            })
        } else {
            cb && cb('expect id but none')
            throw new Error('no id');
        }

    }
}